﻿using SharpDepend;
using SharpDepend.Classes;
using SharpDepend.Datatypes;
using SoundEngine3;
using SoundEngine3.DspPlugins;
using System;
using System.Collections.Generic;
using System.Linq;
using ZquenceStudio3Host.DspPlugins.Synthesizers.Nodesizer.Engine.Commands;
using ZquenceStudio3Host.DspPlugins.Synthesizers.Nodesizer.Engine.GUI.Controls;
using ZquenceStudio3Host.DspPlugins.Synthesizers.Nodesizer.Engine.Modules.Container;
using ZquenceStudio3Host.Graphics.Components;
using ZquenceStudio3Host.Graphics.GUI;
using ZquenceStudio3.Core.Utils;
using ZquenceStudio3.Core.Storage;
using SharpDepend.Storage;
using ZquenceStudio3Host.Resources;

namespace ZquenceStudio3Host.DspPlugins.Synthesizers.Nodesizer.Engine.GUI
{
    public abstract class BaseModulizerUI : GuiDspPlugin, IModulizerUI
    {
        private const int TOP_HEIGHT = 32;
        private const int LEFT_WIDTH = 150;
        private const int PIANO_HEIGHT = 50;
        private const int ELEMENT_HEIGHT = 40;
        private static readonly TimeSpan MENU_ANIMATION_SPEED = new TimeSpan(0, 0, 0, 0, 200);
        private static readonly Color PanelColor = new Color(180, 45, 45, 45);

        private struct TouchedModule
        {
            public Panel Module;
            public Vector2 RelateMousePosAndPanelPos;
        }
        
        private CurrentAnimation<bool> mShowingPianoAnimation;
        private CurrentAnimation<bool> mShowingToolsAnimation;
        private IModuleContainer mCurrentModuleContainer;
        
        internal NodisizerAreaHandler mGrid;
        internal PianoPanel piano;
        internal ScrollPanel toolbar;
        internal Panel sideBar;
        internal Panel innerGuiPanel;
        internal ScrollPanel GuiPanel;
        internal Panel StructurePanel;
        internal Panel ItemsPanel;
        private List<ModulePanel> mModulePanelsList = new List<ModulePanel>();
        private Dictionary<BaseModule, ModulePanel> mModulePanels = new Dictionary<BaseModule, ModulePanel>();

        public IModuleContainer CurrentModuleContainer
        {
            get
            {
                return mCurrentModuleContainer;
            }
        }

        private void AddModulePanel(BaseModule module, ModulePanel panel)
        {
            if (mModulePanels.ContainsKey(module))
            {
                throw new AlreadyExistException("Module panel already exist.");
            }

            mModulePanels.Add(module, panel);
            mModulePanelsList.Add(panel);
        }

        private void RemoveModulePanel(BaseModule module)
        {
            if (!mModulePanels.ContainsKey(module))
            {
                throw new DoNotExistException("Module panel do not exist.");
            }

            ModulePanel panel = mModulePanels[module];
            mModulePanels.Remove(module);
            mModulePanelsList.Remove(panel);
        }

        internal ModulePanel GetModulePanel(BaseModule module)
        {
            if (!mModulePanels.ContainsKey(module))
            {
                return null;
            }

            return mModulePanels[module];
        }

        private List<Panel> mModuleOutputs = new List<Panel>();
        private List<Panel> mModuleInputs = new List<Panel>();
        private Dictionary<ModuleInput, Panel> mMappedInputs = new Dictionary<ModuleInput, Panel>();
        private Dictionary<BaseModule, List<Graphics.Components.Interfaces.IMinMaxValueControl>> mMappedParameters = new Dictionary<BaseModule, List<Graphics.Components.Interfaces.IMinMaxValueControl>>();
        private Dictionary<ModuleOutput, TexturePanel> mMappedOutputs = new Dictionary<ModuleOutput, TexturePanel>();
        private Dictionary<int, TouchedModule> mMouseDownModuleStartPositions = new Dictionary<int, TouchedModule>();

        // Editable variables.
        private ModuleParameter mLastEditedParameter;
        private ModuleIO mLastEditedIO;
        private BaseModule mLastEditedModule;

        // For parameters.
        private Panel       parameterContainer;
        private ValueEditor parameterName;
        private ValueEditor parameterValue;
        private ValueEditor parameterMaxValue;
        private ValueEditor parameterMinValue;
        private Graphics.Components.Button parameterInternally;

        // For module io.
        private Panel moduleIOContainer;
        private ValueEditor moduleIOName;
        private Label moduleIOConnectedTo;
        private Graphics.Components.Button moduleIOInternally;
        private double mDpiScale;

        // For module.
        private Panel moduleContainer;
        private ValueEditor moduleName;

        private SoundEngineContext mApplication;

        private ModuleManager.ModulizerType mType;

        private BaseModulizer mModulizer;

        public bool IsGuiInitialized { get { return Gui != null; } }
        
        public BaseModulizerUI(SoundEngineContext application, ModuleManager.ModulizerType type)
        {
            mApplication = application;
            mType = type;
        }

        internal void UpdateParameterStates()
        {
            mModulizer.UpdateParameterStates();
        }

        protected override void OnSetDspPlugin(BaseDspPluginInstance dsp)
        {
            mModulizer = (BaseModulizer)dsp;
            mModulizer.SetUI(this);
            base.OnSetDspPlugin(dsp);
        }

        protected override void OnShown()
        {
            mGrid.Enabled = true;
            mGrid.TouchEnabled = true;
        }

        protected override void OnHide()
        {
            mGrid.Enabled = false;
            mGrid.TouchEnabled = false;
        }

        protected override void InitializeGuiComponents(Panel gui)
        {
            mDpiScale = Application.DPIScaleFactor;

            gui.BackgroundColor = Color.Transparent;
            gui.Events.OnMousePressing2 += Events_OnMousePressing;
            gui.Events.OnMouseMoving += Events_OnMouseMoving;
            gui.Events.OnMouseUnpressing += Events_OnMouseUnpressing;
            gui.Events.OnScaleChanged += Events_OnScaleChanged;

            mGrid = new NodisizerAreaHandler(this, 100);
            mGrid.SetGraphicsHost(Gui.Display);
            mGrid.Initialize(Application, UndoRedo);

            CreateToolBar();

            CreateSideBar();

            CreatePianoBar();

            // On top.
            CreateGUIPanel();

            lock (SoundEngine3.SoundEngine.SoundLocker)
            {
                RebuildSynthGraph(mModulizer.mModuleManager);
            }

            mGrid.AnimateCameraToFillAllObjects();
        }

        private void CreatePianoBar()
        {
            piano = new PianoPanel(Gui, true);
            piano.BackgroundColor = Color.Black;
            piano.DimensionInProcent = false;
            piano.OnNotePressed += Piano_OnNotePressed;
            piano.OnNoteReleased += Piano_OnNoteReleased;
            //piano.SetView(0, 100);
            Gui.MainPanel.AddComponent(piano);
        }

        bool mouseDown;

        private bool Events_OnMousePressing(object sender, double x, double y, int id)
        {
            BaseObject mainPanel = (BaseObject)sender;

            if (mGrid.IsOverButton(x, y))
            {
                mGrid.TouchEnabled = true;
                return false;
            }

            if (innerGuiPanel.Visible)
            {
                mGrid.TouchEnabled = false;
                return false;
            }

            // If mouse is touched some children, then ignore the background grids.
            bool touched = IsMouseOverAnyChildren(x, y, id, mainPanel);

            foreach (var modulePanel in mModulePanels)
            {
                modulePanel.Value.MouseDown(x, y, id);

                // Force mouse down on input/output panels since they are outside.
                foreach (var moduleInput in modulePanel.Value.ModuleInputPanels)
                {
                    if(moduleInput.Value.MouseDown(x, y, id))
                    {
                        touched = true;
                        break;
                    }
                }
                foreach (var moduleOutput in modulePanel.Value.ModuleOutputPanels)
                {
                    if (moduleOutput.Value.MouseDown(x, y, id))
                    {
                        touched = true;
                        break;
                    }
                }

                // Just to inactivate mGrid.TouchEnable.
                foreach (var moduleOutput in modulePanel.Value.ModuleParameterSliders)
                {
                    if ((moduleOutput.Value as Panel).MouseDown(x, y, id))
                    {
                        touched = true;
                        break;
                    }
                }
            }

            mGrid.TouchEnabled = !touched;

            mouseDown = true;

            return false;
        }

        private void Events_OnMouseMoving(object sender, double x, double y, int id)
        {
            if (mouseDown)
            {
                foreach (var modulePanel in mModulePanels)
                {
                    modulePanel.Value.MouseMove(x, y, id);

                    // Force mouse down on input/output panels since they are outside.
                    foreach (var moduleInput in modulePanel.Value.ModuleInputPanels)
                    {
                        moduleInput.Value.MouseMove(x, y, id);
                    }
                    foreach (var moduleOutput in modulePanel.Value.ModuleOutputPanels)
                    {
                        moduleOutput.Value.MouseMove(x, y, id);
                    }
                }
            }
        }

        private void Events_OnMouseUnpressing(object sender, double x, double y, int id)
        {
            if (mouseDown)
            {
                List<ModulePanel> temp = new List<ModulePanel>(mModulePanels.Values);
                foreach (var modulePanel in temp)
                {
                    modulePanel.MouseUp(x, y, id);

                    // Force mouse down on input/output panels since they are outside.
                    foreach (var moduleInput in modulePanel.ModuleInputPanels)
                    {
                        moduleInput.Value.MouseUp(x, y, id);
                    }
                    foreach (var moduleOutput in modulePanel.ModuleOutputPanels)
                    {
                        moduleOutput.Value.MouseUp(x, y, id);
                    }
                }
            }
        }

        private bool IsMouseOverAnyChildren(double x, double y, int id, BaseObject panel)
        {
            bool touched = false;

            var components = panel.GetComponents();
            foreach (var component in components)
            {
                if (component.IsMouseOver(x, y, id))
                {
                    touched = true;
                    break;
                }
            }
            var parts = panel.GetParts();
            foreach (var part in parts)
            {
                if (part.IsMouseOver(x, y, id))
                {
                    touched = true;
                    break;
                }
            }

            return touched;
        }

        protected override void OnRender(IGraphicsDevice canvas)
        {
            toolbar.Width = canvas.RenderWidth - toolbar.X;
            piano.Width = canvas.RenderWidth;

            // Render grids.
            mGrid.Draw();

            // Render connections.
            Parent parent = new Parent();
            parent.SetResolution(canvas.RenderWidth, canvas.RenderHeight);
            Events_OnRender(canvas, parent);

            // Render the rest of the UI (panels and buttons)
            base.OnRender(canvas);
        }
        
        private void SetPanelAfterH(Panel afterThis, Panel applyOn)
        {
            if (afterThis == null)
            {
                applyOn.X = 2;
                return;
            }
            applyOn.X = afterThis.X + afterThis.Width + 2 * mDpiScale;
            applyOn.Y = afterThis.Y;
            applyOn.Height = afterThis.Height;
        }

        private void SetPanelAfterV(Panel afterThis, Panel applyOn)
        {
            if (afterThis == null)
            {
                applyOn.Y = 2 * mDpiScale;
                return;
            }
            applyOn.Y = afterThis.Y + afterThis.Height + 2 * mDpiScale;
            applyOn.X = afterThis.X;
            applyOn.Width = afterThis.Width;
        }

        private void CreateToolBar()
        {
            Color pressedColor = GlobalColors.BaseAudioColor;

            // Side bar.
            toolbar = new ScrollPanel(Gui);
            toolbar.setBackgroundColor(PanelColor);
            toolbar.DimensionInProcent = false;
            toolbar.X = LEFT_WIDTH * mDpiScale;
            toolbar.Height = TOP_HEIGHT * mDpiScale;
            toolbar.BackgroundColor = PanelColor;
            toolbar.Scrollable = true;
            toolbar.setLockedHeight(true);
            Gui.MainPanel.AddComponent(toolbar);

            Panel previous = null;
            if (mType == ModuleManager.ModulizerType.Synthesizer)
            {
                // Piano.
                Graphics.Components.Button pianoButton = new Graphics.Components.Button(Gui);
                pianoButton.BackgroundColor = new Color(65, 65, 65);
                pianoButton.setBgColorPressed(pressedColor);
                pianoButton.setColorIconPressed(Color.Black);
                pianoButton.Width = 100 * mDpiScale;
                pianoButton.Height = toolbar.Height;
                pianoButton.setTextureIcon("Images/Icons/piano.png");
                pianoButton.ActiveButton = true;
                pianoButton.IsActivated = true;
                pianoButton.OnActivate += delegate
                {
                    ShowPiano();
                };
                pianoButton.OnDeactivate += delegate
                {
                    HidePiano();
                };
                toolbar.AddComponent(pianoButton);
                SetPanelAfterH(null, pianoButton);
                ShowPiano();

                previous = pianoButton;
            }

            // Go out.
            Graphics.Components.Button goOut = new Graphics.Components.Button(Gui);
            goOut.BackgroundColor = new Color(65, 65, 65);
            goOut.setBgColorPressed(pressedColor);
            goOut.setColorIconPressed(Color.Black);
            goOut.Width = 100 * mDpiScale;
            goOut.Height = toolbar.Height;
            goOut.setTextureIcon("Images/Icons/zoomout.png");
            goOut.OnClick += GoOut_OnClick;
            toolbar.AddComponent(goOut);
            SetPanelAfterH(previous, goOut);

            // Fill screen.
            Graphics.Components.Button fillScreen = new Graphics.Components.Button(Gui);
            fillScreen.BackgroundColor = new Color(65, 65, 65);
            fillScreen.setBgColorPressed(pressedColor);
            fillScreen.setColorIconPressed(Color.Black);
            fillScreen.Width = 100 * mDpiScale;
            fillScreen.Height = toolbar.Height;
            fillScreen.setTextureIcon("Images/Icons/icon_fillscreen.png");
            fillScreen.OnClick += delegate
            {
                mGrid.AnimateCameraToFillAllObjects();
            };
            toolbar.AddComponent(fillScreen);
            SetPanelAfterH(goOut, fillScreen);

            Graphics.Components.Button pan = new Graphics.Components.Button(Gui);
            Graphics.Components.Button select = new Graphics.Components.Button(Gui);
            Graphics.Components.Button add = new Graphics.Components.Button(Gui);

            // Pan.
            pan.BackgroundColor = new Color(65, 65, 65);
            pan.setBgColorPressed(pressedColor);
            pan.setColorIconPressed(Color.Black);
            pan.Width = 100 * mDpiScale;
            pan.setTextureIcon("Images/Icons/move2.png");
            pan.ActiveButton = true;
            pan.IsActivated = true;
            pan.OnActivate += delegate
            {
                mLastToolUsed = pan;
                select.IsActivated = false;
                add.IsActivated = false;
                mGrid.Tool = TrackArea.ToolButton.None;
            };
            pan.OnDeactivate += delegate
            {
                if (pan == mLastToolUsed)
                {
                    pan.ActivateWithoutEvents();
                }
            };
            toolbar.AddComponent(pan);
            SetPanelAfterH(fillScreen, pan);
            // Select.
            select.BackgroundColor = new Color(65, 65, 65);
            select.setBgColorPressed(pressedColor);
            select.setColorIconPressed(Color.Black);
            select.Width = 100 * mDpiScale;
            select.setTextureIcon("Images/Icons/icon_select.png");
            select.ActiveButton = true;
            select.IsActivated = false;
            select.OnActivate += delegate
            {
                mLastToolUsed = select;
                pan.IsActivated = false;
                add.IsActivated = false;
                mGrid.Tool = TrackArea.ToolButton.Selector;
            };
            select.OnDeactivate += delegate
            {
                if (select == mLastToolUsed)
                {
                    select.ActivateWithoutEvents();
                }
            };
            toolbar.AddComponent(select);
            SetPanelAfterH(pan, select);
            // Pen.
            add.BackgroundColor = new Color(65, 65, 65);
            add.setBgColorPressed(pressedColor);
            add.setColorIconPressed(Color.Black);
            add.Width = 100 * mDpiScale;
            add.setTextureIcon("Images/Icons/pen.png");
            add.ActiveButton = true;
            add.IsActivated = false;
            add.OnActivate += delegate
            {
                mLastToolUsed = add;
                pan.IsActivated = false;
                select.IsActivated = false;
                mGrid.Tool = TrackArea.ToolButton.Pen;
            };
            add.OnDeactivate += delegate
            {
                if (add == mLastToolUsed)
                {
                    add.ActivateWithoutEvents();
                }
            };
            toolbar.AddComponent(add);
            SetPanelAfterH(select, add);
        }

        private Button mLastToolUsed;
        private bool mToolsShown;
        private bool mPianoShown;

        private void Events_OnScaleChanged()
        {
            ShowToolsAnimation(mToolsShown);
            ShowPianoAnimation(mPianoShown);

            if (mCallShowUI)
            {
                ShowUI();
                mCallShowUI = false;
            }

            ItemsPanel.Height = Gui.MainPanel.Height - moduleList.RealY;
            moduleList.Height = Gui.MainPanel.Height - moduleList.RealY - PIANO_HEIGHT * mDpiScale - 10;
        }

        private void ShowPiano()
        {
            ShowPianoAnimation(true);
        }

        private void HidePiano()
        {
            ShowPianoAnimation(false);
        }

        private void ShowPianoAnimation(bool visibility)
        {
            mPianoShown = visibility;
            if (mShowingPianoAnimation == null)
            {
                mShowingPianoAnimation = AnimationUtils<bool>.Run(MENU_ANIMATION_SPEED, PianoShowingTick, AnimationBase.Tanh, visibility);
            }
            else
            {
                // Ensure to stop if it is currently animating.
                AnimationUtils<bool>.Stop(mShowingPianoAnimation);

                AnimationUtils<bool>.ReuseRun(mShowingPianoAnimation, MENU_ANIMATION_SPEED, PianoShowingTick, visibility);
            }
        }

        private void PianoShowingTick(double value, bool finish, bool visibility)
        {
            if (visibility)
            {
                piano.Height = PIANO_HEIGHT * mDpiScale * value;
                piano.Y = Gui.Display.GraphicsDevice.RenderHeight - piano.Height;

                piano.Visible = true;
                piano.Intersectable = true;
            }
            else
            {
                piano.Height = PIANO_HEIGHT * mDpiScale * (1.0 - value);
                piano.Y = Gui.Display.GraphicsDevice.RenderHeight - piano.Height;

                if (finish)
                {
                    piano.Visible = false;
                    piano.Intersectable = false;
                }
            }
        }

        private void ShowTools()
        {
            toolsVisibility.setText("Hide");
            ShowToolsAnimation(true);
        }

        private void HideTools()
        {
            toolsVisibility.setText("Show");
            ShowToolsAnimation(false);
        }

        private void ShowToolsAnimation(bool visibility)
        {
            mToolsShown = visibility;

            if (mShowingToolsAnimation == null)
            {
                mShowingToolsAnimation = AnimationUtils<bool>.Run(MENU_ANIMATION_SPEED, ToolsShowingTick, AnimationBase.Tanh, visibility);
            }
            else
            {
                // Ensure to stop if it is currently animating.
                AnimationUtils<bool>.Stop(mShowingToolsAnimation);

                AnimationUtils<bool>.ReuseRun(mShowingToolsAnimation, MENU_ANIMATION_SPEED, ToolsShowingTick, visibility);
            }
        }

        private void ToolsShowingTick(double value, bool finish, bool visibility)
        {
            var graphicsHost = GraphicsHost;
            if (graphicsHost != null)
            {
                if (visibility)
                {
                    sideBar.Height = graphicsHost.GraphicsDevice.RenderHeight * value + TOP_HEIGHT * mDpiScale;
                }
                else
                {
                    sideBar.Height = graphicsHost.GraphicsDevice.RenderHeight * (1.0 - value) + TOP_HEIGHT * mDpiScale;
                }
            }
        }

        /// <summary>
        /// TODO: Fixa MakeGroup så det fungerar.
        /// </summary>
        /// <param name="modulePanels"></param>
        public void MakeGroup(IEnumerable<ModulePanel> modulePanels)
        {
            List<BaseModule> modules = new List<BaseModule>(modulePanels.Count());
            foreach (var modulePanel in modulePanels)
            {
                modules.Add(modulePanel.Module);
            }

            MakeGroupCommand command = new MakeGroupCommand(mGrid, this, modules);
            UndoRedo.AddAndRunCommand(command);

            mGrid.Editor.SelectorHandler.ClearSelection();
        }

        public void GoInto(BaseModule module)
        {
            RebuildSynthGraph(module as IModuleContainer);

            mGrid.Editor.SelectorHandler.ClearSelection();

            mGrid.AnimateCameraToFillAllObjects();
        }

        private void Piano_OnNotePressed(int note, int velocity)
        {
            mModulizer.PressNote((byte)note, (byte)velocity, 0.0);
        }

        private void Piano_OnNoteReleased(int note)
        {
            mModulizer.ReleaseNote((byte)note, 0, 0.0);
        }
        
        private void ReloadTest_OnClick(object sender, double x, double y, int id)
        {
            var currentModuleContainer = mCurrentModuleContainer;
            if (currentModuleContainer != null)
            {
                RebuildSynthGraph(currentModuleContainer);
            }
        }

        /// <summary>
        /// Calls when user want go outside the current module.
        /// </summary>
        private void GoOut_OnClick(object sender, double x, double y, int id)
        {
            var currentModuleContainer = mCurrentModuleContainer;
            if (currentModuleContainer != null && currentModuleContainer.ParentModule is IModuleContainer)
            {
                RebuildSynthGraph(currentModuleContainer.ParentModule as IModuleContainer);

                mGrid.Editor.SelectorHandler.ClearSelection();

                mGrid.AnimateCameraToFillAllObjects();
            }
        }

        Graphics.Components.Button toolsVisibility;

        private void CreateSideBar()
        {
            // Side bar.
            sideBar = new Panel(Gui);
            sideBar.DimensionInProcent = false;
            sideBar.Width = LEFT_WIDTH * mDpiScale;
            sideBar.BackgroundColor = PanelColor;
            Gui.MainPanel.AddComponent(sideBar);

            double buttonWidth = LEFT_WIDTH * 0.5 * mDpiScale;
            double buttonHeight = TOP_HEIGHT * mDpiScale;
            double buttonX1 = 0;
            double buttonX2 = LEFT_WIDTH * 0.5 * mDpiScale;
            double buttonY1 = 0;
            double buttonY2 = TOP_HEIGHT * mDpiScale;

            // Gui button.
            Graphics.Components.Button guiButton = new Graphics.Components.Button(Gui);
            guiButton.BackgroundColor = new Color(65, 65, 65);
            guiButton.Label.Text = "UI";
            guiButton.Y = buttonY2;
            guiButton.X = buttonX2;
            guiButton.Width = buttonWidth;
            guiButton.Height = buttonHeight;
            guiButton.OnClick += delegate
            {
                ShowUI();
            };
            sideBar.AddComponent(guiButton);

            // Structure button
            Graphics.Components.Button structureButton = new Graphics.Components.Button(Gui);
            structureButton.BackgroundColor = new Color(65, 65, 65);
            structureButton.setTextureIcon("Images/Icons/icon_ham_preference.png");
            structureButton.Y = buttonY1;
            structureButton.X = buttonX2;
            structureButton.Width = buttonWidth;
            structureButton.Height = buttonHeight;
            structureButton.OnClick += delegate
            {
                //GuiPanel.Visible = false;
                StructurePanel.Visible = true;
                ItemsPanel.Visible = false;
                //innerGuiPanel.Visible = true;
            };
            sideBar.AddComponent(structureButton);

            // Items button.
            Graphics.Components.Button itemsButton = new Graphics.Components.Button(Gui);
            itemsButton.BackgroundColor = new Color(65, 65, 65);
            //itemsButton.Label.Text = "Modules";
            itemsButton.setTextureIcon("Images/Icons/tools.png");
            itemsButton.Y = buttonY2;
            itemsButton.X = buttonX1;
            itemsButton.Width = buttonWidth;
            itemsButton.Height = buttonHeight;
            itemsButton.OnClick += delegate
            {
                //GuiPanel.Visible = false;
                StructurePanel.Visible = false;
                ItemsPanel.Visible = true;
            };
            sideBar.AddComponent(itemsButton);

            // Tools visibility button.
            toolsVisibility = new Graphics.Components.Button(Gui);
            toolsVisibility.BackgroundColor = new Color(65, 65, 65);
            toolsVisibility.Y = buttonY1;
            toolsVisibility.X = buttonX1;
            toolsVisibility.Width = buttonWidth;
            toolsVisibility.Height = buttonHeight;
            toolsVisibility.setText("Show");
            toolsVisibility.ActiveButton = true;
            toolsVisibility.IsActivated = true;
            toolsVisibility.OnActivate += delegate
            {
                ShowTools();
            };
            toolsVisibility.OnDeactivate += delegate
            {
                HideTools();
            };
            sideBar.AddComponent(toolsVisibility);
            ShowTools();

            CreateStructurePanel(buttonY2 + buttonHeight + 5);
            CreateItemsPanel(buttonY2 + buttonHeight + 5);
            
            // Show modules panel first.
            //GuiPanel.Visible = false;
            StructurePanel.Visible = false;
            ItemsPanel.Visible = true;
        }

        private bool mCallShowUI;

        private void ShowUI()
        {
            if (Gui == null)
            {
                mCallShowUI = true;
            }
            else
            {
                //GuiPanel.Visible = true;
                innerGuiPanel.X = 0;
                innerGuiPanel.Visible = true;

                //StructurePanel.Visible = false;
                //ItemsPanel.Visible = false;
                BuildParameters(GuiPanel, false);
            }
        }
        
        private void CreateGUIPanel()
        {
            innerGuiPanel = new Panel(Gui);
            innerGuiPanel.BackgroundColor = Color.Black;
            innerGuiPanel.DimensionInProcent = true;
            innerGuiPanel.Width = 100;
            innerGuiPanel.Height = 100;
            innerGuiPanel.X = -10000000000;
            innerGuiPanel.Visible = false;

            Button back = new Button(Gui);
            back.DimensionInProcent = true;
            back.Width = 15;
            back.Height = 10;
            back.setTextureIcon("Images/Icons/tools.png");
            back.OnClicked += delegate
            {
                innerGuiPanel.Visible = false;
                innerGuiPanel.X = -1000000000;
            };
            innerGuiPanel.AddComponent(back);

            GuiPanel = new ScrollPanel(Gui);
            GuiPanel.setLockedWidth(true);
            GuiPanel.DimensionInProcent = true;
            GuiPanel.Y = 10;
            GuiPanel.Width = 100;
            GuiPanel.Height = 90;
            GuiPanel.BackgroundColor = Color.Transparent;
            innerGuiPanel.AddComponent(GuiPanel);

            Gui.MainPanel.AddComponent(innerGuiPanel);
        }

        private void CreateStructurePanel(double positionY)
        {
            StructurePanel = new Panel(Gui);
            StructurePanel.X = 2 * mDpiScale;
            StructurePanel.Y = positionY;
            StructurePanel.Width = sideBar.Width - (StructurePanel.X * 2);
            StructurePanel.Height = 2000 * mDpiScale;
            StructurePanel.BackgroundColor = Color.Transparent;
            sideBar.AddComponent(StructurePanel);

            ParameterStructureContainer();

            ModuleIOStructureContainer();

            ModuleStructureContainer();
        }

        private void ModuleStructureContainer()
        {
            moduleContainer = new Panel(Gui);
            moduleContainer.X = 2 * mDpiScale;
            moduleContainer.Y = 0;
            moduleContainer.Width = sideBar.Width - (moduleContainer.X * 2);
            moduleContainer.Height = 2000 * mDpiScale;
            moduleContainer.BackgroundColor = Color.Transparent;
            moduleContainer.Visible = false;
            StructurePanel.AddComponent(moduleContainer);
            moduleName = new ValueEditor(Gui, "Name:");
            moduleName.X = 2 * mDpiScale;
            moduleName.Y = 120 * mDpiScale;
            moduleName.Width = LEFT_WIDTH * mDpiScale;
            moduleName.Height = ELEMENT_HEIGHT;
            moduleName.OnTextChanged += delegate (string text)
            {
                SetName(mLastEditedModule, text);
            };
            moduleContainer.AddComponent(moduleName);
            SetPanelAfterV(null, moduleName);
        }

        private void ModuleIOStructureContainer()
        {
            moduleIOContainer = new Panel(Gui);
            moduleIOContainer.X = 2 * mDpiScale;
            moduleIOContainer.Y = 0;
            moduleIOContainer.Width = sideBar.Width - (moduleIOContainer.X * 2);
            moduleIOContainer.Height = 2000 * mDpiScale;
            moduleIOContainer.BackgroundColor = Color.Transparent;
            moduleIOContainer.Visible = false;
            StructurePanel.AddComponent(moduleIOContainer);
            moduleIOName = new ValueEditor(Gui, "Name:");
            moduleIOName.X = 2 * mDpiScale;
            moduleIOName.Y = 120 * mDpiScale;
            moduleIOName.Width = LEFT_WIDTH * mDpiScale;
            moduleIOName.Height = ELEMENT_HEIGHT;
            moduleIOName.OnTextChanged += delegate (string text)
            {
                SetName(mLastEditedIO, text);
            };
            moduleIOContainer.AddComponent(moduleIOName);
            SetPanelAfterV(null, moduleIOName);
            moduleIOConnectedTo = new Label(Gui);
            moduleIOConnectedTo.Height = ELEMENT_HEIGHT;
            moduleIOContainer.AddComponent(moduleIOConnectedTo);
            SetPanelAfterV(moduleIOName, moduleIOConnectedTo);
            moduleIOInternally = new Graphics.Components.Button(Gui);
            moduleIOInternally.SetButtonColor(new Color(15, 15, 15));
            moduleIOInternally.Height = ELEMENT_HEIGHT;
            moduleIOInternally.ActiveButton = true;
            moduleIOInternally.Label.Text = "Internally";
            moduleIOInternally.OnDeactivate += delegate
            {
                mLastEditedIO.InternallyVisible = moduleIOInternally.ActivateOn;

                mModulizer.UpdateParameterStates();
            };
            moduleIOInternally.OnActivate += delegate
            {
                mLastEditedIO.InternallyVisible = moduleIOInternally.ActivateOn;

                mModulizer.UpdateParameterStates();
            };
            moduleIOContainer.AddComponent(moduleIOInternally);
            SetPanelAfterV(moduleIOConnectedTo, moduleIOInternally);
        }

        private void ParameterStructureContainer()
        {
            parameterContainer = new Panel(Gui);
            parameterContainer.X = 2 * mDpiScale;
            parameterContainer.Y = 0;
            parameterContainer.Width = sideBar.Width - (parameterContainer.X * 2);
            parameterContainer.Height = 2000 * mDpiScale;
            parameterContainer.BackgroundColor = Color.Transparent;
            parameterContainer.Visible = false;
            StructurePanel.AddComponent(parameterContainer);
            parameterName = new ValueEditor(Gui, "Name:");
            parameterName.X = 2 * mDpiScale;
            parameterName.Y = 5 * mDpiScale;
            parameterName.Width = LEFT_WIDTH * mDpiScale;
            parameterName.Height = ELEMENT_HEIGHT;
            parameterName.OnTextChanged += delegate (string text)
            {
                SetName(mLastEditedParameter, text);
            };
            parameterContainer.AddComponent(parameterName);
            SetPanelAfterV(null, parameterName);
            parameterValue = new ValueEditor(Gui, "Value:");
            parameterValue.Height = ELEMENT_HEIGHT;
            parameterValue.OnTextChanged += delegate (string text)
            {
                if (mLastEditedParameter.Flags == SoundEngine3.DspPlugins.Meta.ParameterPropertiesFlags.ParameterString)
                {
                    mLastEditedParameter.RawObjectValue = text;
                }
                else
                {
                    double value;
                    if (double.TryParse(text, out value))
                    {
                        mLastEditedParameter.RawValue = value;

                        foreach (var dial in GetBindedParameters(mLastEditedParameter))
                        {
                            dial.Value = value;

                            if (dial is HorizontalSlider)
                            {
                                parameterValue.IsValid = true;
                            }
                        }
                    }
                    else
                    {
                        parameterValue.IsValid = false;
                    }
                }
            };
            parameterContainer.AddComponent(parameterValue);
            SetPanelAfterV(parameterName, parameterValue);
            parameterMaxValue = new ValueEditor(Gui, "Max value:");
            parameterMaxValue.Height = ELEMENT_HEIGHT;
            parameterMaxValue.OnTextChanged += delegate (string text)
            {
                double value;
                if (double.TryParse(text, out value))
                {
                    mLastEditedParameter.MaxValue = value;

                    foreach (var dial in GetBindedParameters(mLastEditedParameter))
                    {
                        dial.MaxValue = value;

                        if (dial is HorizontalSlider)
                        {
                            parameterMaxValue.IsValid = true;
                        }
                    }
                }
                else
                {
                    parameterMaxValue.IsValid = false;
                }
            };
            parameterContainer.AddComponent(parameterMaxValue);
            SetPanelAfterV(parameterValue, parameterMaxValue);
            parameterMinValue = new ValueEditor(Gui, "Min value:");
            parameterMinValue.Height = ELEMENT_HEIGHT;
            parameterMinValue.OnTextChanged += delegate (string text)
            {
                double value;
                if (double.TryParse(text, out value))
                {
                    mLastEditedParameter.MinValue = value;

                    foreach (var dial in GetBindedParameters(mLastEditedParameter))
                    {
                        dial.MinValue = value;

                        if (dial is HorizontalSlider)
                        {
                            parameterMinValue.IsValid = true;
                        }
                    }
                }
                else
                {
                    parameterMinValue.IsValid = false;
                }
            };
            parameterContainer.AddComponent(parameterMinValue);
            SetPanelAfterV(parameterMaxValue, parameterMinValue);
            parameterInternally = new Graphics.Components.Button(Gui);
            parameterInternally.SetButtonColor(new Color(15, 15, 15));
            parameterInternally.Height = ELEMENT_HEIGHT;
            parameterInternally.ActiveButton = true;
            parameterInternally.Label.Text = "Internally";
            parameterInternally.OnDeactivate += delegate
            {
                mLastEditedParameter.InternallyVisible = parameterInternally.ActivateOn;

                mModulizer.UpdateParameterStates();
            };
            parameterInternally.OnActivate += delegate
            {
                mLastEditedParameter.InternallyVisible = parameterInternally.ActivateOn;

                mModulizer.UpdateParameterStates();
            };
            parameterContainer.AddComponent(parameterInternally);
            SetPanelAfterV(parameterMinValue, parameterInternally);
        }

        private void SetName(ModuleParameter parameter, string name)
        {
            parameter.Label = name;

            RefreshModuleLabels(parameter.Module);
        }

        private void SetName(BaseModule module, string name)
        {
            module.Name = name;

            RefreshModuleLabels(module);
        }

        private void SetName(ModuleIO moduleIO, string name)
        {
            moduleIO.Name = name;
            
            RefreshModuleLabels(moduleIO.Module);
        }

        private void RefreshModuleLabels(BaseModule module)
        {
            if (mModulePanels.ContainsKey(module))
            {
                mModulePanels[module].RefreshLabels();
            }
        }

        Graphics.Components.Listbox moduleList;

        private void CreateItemsPanel(double positionY)
        {
            ItemsPanel = new Panel(Gui);
            ItemsPanel.X = 2 * mDpiScale;
            ItemsPanel.Y = positionY;
            ItemsPanel.Width = 196 * mDpiScale;
            ItemsPanel.BackgroundColor = Color.Transparent;
            sideBar.AddComponent(ItemsPanel);

            // Add module list.
            moduleList = new Listbox(Gui);
            moduleList.SetBackgroundColor(new Color(80, 40, 40, 40));
            moduleList.Width = 196 * mDpiScale;
            moduleList.Y = 0;

            moduleList.BackgroundColor = new Color(LEFT_WIDTH, 30, 30, 30);

            List<ModulesRegistration.ModuleInfo> modules = new List<ModulesRegistration.ModuleInfo>(ModulesRegistration.Modules);
            modules.Sort();
            foreach (var moduleInfo in modules)
            {
                ListboxItem item = new ListboxItem(Gui, moduleInfo.Display, moduleInfo.ModuleType);
                item.ItemPanel.Height = 23;
                moduleList.AddItem(item);
            }
            ItemsPanel.AddComponent(moduleList);
        }
        
        public void RefreshPositions()
        {
            foreach (var module in mCurrentModuleContainer.SubModules)
            {
                Vector4 position = module.GuiPosition;

                ModulePanel modulePanel = GetModulePanel(module);

                if (modulePanel != null)
                {
                    modulePanel.Start = position.X;
                    modulePanel.HeightPosition = position.Y;
                    modulePanel.DisplayLength = position.Width;
                    modulePanel.HeightPosition = position.Height;
                }
            }
        }

        public void RebuildSynthGraphModuleParent(BaseModule module)
        {
            if (module == null)
            {
                throw new ArgumentNullException(nameof(module));
            }

            BaseModule parent = module.ParentModule;

            if (parent == null)
            {
                throw new ArgumentNullException("module.ParentModule");
            }

            // Only update if we are not inside it.
            if (mCurrentModuleContainer != parent)
            {
                GoInto(parent);
            }
            else
            {
                RebuildSynthGraph(parent as IModuleContainer);
            }
        }

        public void RebuildSynthGraph()
        {
            RebuildSynthGraph(mCurrentModuleContainer);
        }

        /// <summary>
        /// TODO: Gör så att vi kan se alla Inputs & Outputs, Alla parametrar som små DialButtons.
        /// Och sen ska vi kunna lägga till/ta bort och koppla om alla moduler :D
        /// </summary>
        public void RebuildSynthGraph(IModuleContainer moduleContainer)
        {
            if (moduleContainer == null)
            {
                throw new ArgumentNullException(nameof(moduleContainer));
            }

            if (IsGuiInitialized)
            {
                // Remove all from current area.
                for (int i = mModulePanelsList.Count - 1; i >= 0; i--)
                {
                    ModulePanel modulePanel = mModulePanelsList[i];

                    DestroyModulePanel(modulePanel.Module);
                }

                // Add all from the container.
                for (int i = 0; i < moduleContainer.SubModules.Count; i++)
                {
                    BaseModule module = moduleContainer.SubModules[i];

                    ModulePanel panel = GetModulePanel(module);

                    if (panel == null)
                    {
                        CreateModulePanel(module);
                    }
                    else
                    {
                        ShowModulePanel(panel);
                    }
                }
            }

            // Tell now who is the current module to be inside.
            mCurrentModuleContainer = moduleContainer;
        }
        
        internal ModulePanel CreateModulePanel(BaseModule module)
        {
            ModulePanel modulePanel = new ModulePanel(this, module);

            // Add modules parameters to the dsp parameter list.
            //CreateParameters(module);

            AddModulePanel(modulePanel);

            return modulePanel;
        }

        internal void AddModulePanel(ModulePanel modulePanel)
        {
            BaseModule module = modulePanel.Module;

            // Module.
            AddModulePanel(module, modulePanel);

            // Set position.
            modulePanel.Start = module.GuiPosition.X;
            modulePanel.HeightPosition = module.GuiPosition.Y;
            modulePanel.DisplayLength = module.GuiPosition.Width;
            modulePanel.DisplayHeight = module.GuiPosition.Height;

            // Parameters.
            //if (!(module is IModuleContainer))
            {
                foreach (var parameter in modulePanel.ModuleParameterSliders)
                {
                    if (!mMappedParameters.ContainsKey(module))
                    {
                        mMappedParameters.Add(module, new List<Graphics.Components.Interfaces.IMinMaxValueControl>());
                    }
                    mMappedParameters[module].Add(parameter.Value);

                    BindParameter(parameter.Key, parameter.Value, true); // Ignore check, we know that the parameter exist, it may be invisible some times.

                    parameter.Value.OnValueChanged += Dial_OnValueChanged;
                }
            }

            ShowModulePanel(modulePanel);
        }

        internal void ShowModulePanel(ModulePanel modulePanel)
        {
            // Make sure to clean every panels before.
            HideModulePanel(modulePanel);

            BaseModule module = modulePanel.Module;

            modulePanel.Visible = true;

            // Connect input panel events.
            foreach (var inputPanel in modulePanel.ModuleInputPanels)
            {
                inputPanel.Value.Events.OnMousePressing += Input_Pressed;
                inputPanel.Value.Events.OnMouseMoving += Input_Moving;
                inputPanel.Value.Events.OnMouseUnpressing += Input_Released;
            }

            // Caching
            {
                // Inputs.
                foreach (var inputPanel in modulePanel.ModuleInputPanels)
                {
                    mModuleInputs.Add(inputPanel.Value);
                    mMappedInputs.Add(inputPanel.Key, inputPanel.Value);
                }

                // Outputs.
                foreach (var outputPanel in modulePanel.ModuleOutputPanels)
                {
                    outputPanel.Value.Events.OnMousePressing += Output_Pressed;
                    mModuleOutputs.Add(outputPanel.Value);
                    mMappedOutputs.Add(outputPanel.Key, outputPanel.Value);
                }

                // Current visible outputs.
                //mCurrentModuleContainerVisibleOutputs.Add(module, modulePanel.ModuleOutputPanels.Keys);
            }
        }

        internal void DestroyModulePanel(BaseModule module)
        {
            if (!mModulePanels.ContainsKey(module))
            {
                throw new DoNotExistException("Can not remove module from UI, module do not exist.");
            }

            ModulePanel modulePanel = mModulePanels[module];

            // Remove parameters.
            //RemoveParameters(module);

            RemoveModulePanel(modulePanel);
        }

        internal void RemoveModulePanel(ModulePanel modulePanel)
        {
            BaseModule module = modulePanel.Module;

            // Module.
            RemoveModulePanel(module);

            // Parameters.
            if (!(module is IModuleContainer))
            {
                foreach (var parameter in modulePanel.ModuleParameterSliders)
                {
                    if (mMappedParameters.ContainsKey(module))
                    {
                        if (mMappedParameters[module].Count > 1)
                        {
                            mMappedParameters[module].Remove(parameter.Value);
                        }
                        else
                        {
                            mMappedParameters[module].Remove(parameter.Value);
                            mMappedParameters.Remove(module);
                        }
                    }

                    UnbindParameter(parameter.Key);

                    parameter.Value.OnValueChanged -= Dial_OnValueChanged;
                }
            }

            HideModulePanel(modulePanel);
        }

        internal void HideModulePanel(ModulePanel modulePanel)
        {
            BaseModule module = modulePanel.Module;

            modulePanel.Visible = false;

            // Disconnect input panel events.
            foreach (var inputPanel in modulePanel.ModuleInputPanels)
            {
                inputPanel.Value.Events.OnMousePressing -= Input_Pressed;
                inputPanel.Value.Events.OnMouseMoving -= Input_Moving;
                inputPanel.Value.Events.OnMouseUnpressing -= Input_Released;
            }

            // Remove caching
            {
                // Inputs.
                foreach (var inputPanel in modulePanel.ModuleInputPanels)
                {
                    mModuleInputs.Remove(inputPanel.Value);
                    mMappedInputs.Remove(inputPanel.Key);
                }

                // Outputs.
                foreach (var outputPanel in modulePanel.ModuleOutputPanels)
                {
                    outputPanel.Value.Events.OnMousePressing -= Output_Pressed;
                    mModuleOutputs.Remove(outputPanel.Value);
                    mMappedOutputs.Remove(outputPanel.Key);
                }

                // Current visible outputs.
                //mCurrentModuleContainerVisibleOutputs.Remove(module);
            }
        }

        internal void UpdateModulePanel(ModulePanel modulePanel)
        {
            bool wasVisible = false;
            if (modulePanel.Visible)
            {
                wasVisible = true;

                // Remove input/output events and so on.
                HideModulePanel(modulePanel);
            }

            // Rebuild panel.
            modulePanel.ReBuildComponents();

            if (wasVisible)
            {
                // Add input/output events again.
                ShowModulePanel(modulePanel);
            }
        }

        /// <summary>
        /// Create a module from the currently selected type.
        /// </summary>
        /// <returns>Module panel.</returns>
        internal BaseModule CreateModule()
        {
            var item = moduleList.GetSelectedItem();

            if (item != null)
            {
                lock (SoundEngine3.SoundEngine.SoundLocker)
                {
                    Type selectedObject = (Type)item.ItemObject;

                    BaseModule module = ModulesRegistration.CreateInstance(selectedObject, mModulizer.mModuleManager);

                    mCurrentModuleContainer.AddSubModule(module);
                    
                    return module;
                }
            }

            return null;
        }

        public int ModulePanelCount
        {
            get
            {
                return mModulePanelsList.Count;
            }
        }

        public BaseModulizer Modulizer { get { return mModulizer; } }

        public ModulePanel GetModulePanel(int index)
        {
            return mModulePanelsList[index];
        }

        private void Dial_OnValueChanged(object sender, double value)
        {
            HorizontalSlider button = (HorizontalSlider)sender;
            ModuleParameter parameter = (ModuleParameter)button.Tag;

            UpdateParameterForEdit(parameter);

            parameter.RawValue = value;
        }

        public void UpdateParameterForEdit(ModuleParameter parameter)
        {
            mLastEditedParameter = parameter;

            parameterName.Text = parameter.Label.ToString();

            if (parameter.Flags == SoundEngine3.DspPlugins.Meta.ParameterPropertiesFlags.ParameterString)
            {
                parameterValue.Text = parameter.RawObjectValue.ToString();
                parameterMinValue.Visible = false;
                parameterMaxValue.Visible = false;

                parameterValue.AutoSize = false;
                parameterValue.textbox.Height = 100;
                parameterValue.Height = 120;
            }
            else
            {
                parameterValue.Text = parameter.RawValue.ToString();
                parameterMinValue.Text = parameter.MinValue.ToString();
                parameterMaxValue.Text = parameter.MaxValue.ToString();
                parameterMinValue.Visible = true;
                parameterMaxValue.Visible = true;

                parameterValue.AutoSize = true;
                parameterValue.textbox.Height = 20;
                parameterValue.Height = 40;
            }

            parameterValue.TitleHint = "Value";
            parameterValue.DescriptionHint = string.Format("Value of module parameter '{0}'", parameter.Label);
            parameterMinValue.TitleHint = "Min value";
            parameterMinValue.DescriptionHint = string.Format("Min value of module parameter '{0}'", parameter.Label);
            parameterMaxValue.TitleHint = "Max value";
            parameterMaxValue.DescriptionHint = string.Format("Max value of module parameter '{0}'", parameter.Label);

            if (parameter.InternallyVisible)
            {
                parameterInternally.ActivateWithoutEvents();
            }
            else
            {
                parameterInternally.UnActivateWithoutEvents();
            }

            parameterContainer.Visible = true;
            moduleIOContainer.Visible = false;
            moduleContainer.Visible = false;
        }

        public void UpdateModuleIOForEdit(ModuleIO moduleIO)
        {
            mLastEditedIO = moduleIO;

            moduleIOName.Text = moduleIO.Name;
            moduleIOName.TitleHint = "Name";

            if (moduleIO is ModuleInput)
            {
                moduleIOName.DescriptionHint = string.Format("Input name of module '{0}'", moduleIO.Name);
            }
            else
            {
                moduleIOName.DescriptionHint = string.Format("Output name of module '{0}'", moduleIO.Name);
            }

            if (moduleIO.InternallyVisible)
            {
                moduleIOInternally.ActivateWithoutEvents();
            }
            else
            {
                moduleIOInternally.UnActivateWithoutEvents();
            }

            if (moduleIO is ModuleInput)
            {
                ModuleInput input = (ModuleInput)moduleIO;
                moduleIOConnectedTo.Text = input.FromOutput ==  null ? "Not connected" : input.FromOutput.Name;
            }
            else
            {
                ModuleOutput output = (ModuleOutput)moduleIO;
                moduleIOConnectedTo.Text = output.ConnectedInputs.Count + " connected." ;
            }

            parameterContainer.Visible = false;
            moduleIOContainer.Visible = true;
            moduleContainer.Visible = false;
        }

        internal void UpdateModuleForEdit(BaseModule module)
        {
            mLastEditedModule = module;

            moduleName.Text = module.Name;
            moduleName.TitleHint = "Name";
            moduleName.DescriptionHint = string.Format("name of module '{0}'", module.Name);

            parameterContainer.Visible = false;
            moduleIOContainer.Visible = false;
            moduleContainer.Visible = true;
        }

        /// <summary>
        /// Calls when a mouse id pressed on a output panel.
        /// </summary>
        private void Output_Pressed(object sender, double x, double y, int id)
        {
            Panel panel = (Panel)sender;
            ModuleOutput output = (ModuleOutput)panel.Tag;

            UpdateModuleIOForEdit(output);
        }

        Panel mCurrentPanelMouseDown;

        /// <summary>
        /// Calls when a mouse id pressed on a input panel.
        /// </summary>
        private void Input_Pressed(object sender, double x, double y, int id)
        {
            Panel panel = (Panel)sender;
            
            if (panel.IsMouseOver(x, y, id))
            {
                ModuleInput input = (ModuleInput)panel.Tag;

                UpdateModuleIOForEdit(input);

                mCurrentPanelMouseDown = panel;
            }
            panel.AlwaysTouchable = false;
        }

        /// <summary>
        /// Calls when a mouse id moving on a output panel.
        /// (Drag a line to an input)
        /// </summary>
        private void Input_Moving(object sender, double x, double y, int id)
        {
            Panel panel = (Panel)sender;

            if (mCurrentPanelMouseDown == panel)
            {
                ModuleInput input = (ModuleInput)panel.Tag;

                foreach (var outputPanel in mModuleOutputs)
                {
                    ModuleOutput output = outputPanel.Tag as ModuleOutput;

                    if (outputPanel.IsMouseOver(x, y, id) && output.PossibleToConnect(input))
                    {
                        // Highlight.
                        outputPanel.BackgroundColor = SharpDepend.Datatypes.Color.Red;
                    }
                    else
                    {
                        // Normal.
                        outputPanel.BackgroundColor = GetColor(output);
                    }
                }
            }
        }

        private void Input_Released(object sender, double x, double y, int id)
        {
            Panel panel = (Panel)sender;

            if (mCurrentPanelMouseDown == panel)
            {
                ModuleInput input = (ModuleInput)panel.Tag;

                foreach (var outputPanel in mModuleOutputs)
                {
                    if (outputPanel.IsMouseOver(x, y, id))
                    {
                        ModuleOutput output = (ModuleOutput)outputPanel.Tag;

                        lock (SoundEngine3.SoundEngine.SoundLocker)
                        {
                            // Drop input to this output.
                            if (!output.ConnectedInputs.Contains(input))
                            {
                                if (output.PossibleToConnect(input))
                                {
                                    Connect(output, input);

                                    GetModulePanel(output.Module)?.UpdateColors();
                                    GetModulePanel(input.Module)?.UpdateColors();
                                }
                            }
                        }

                        // Normal.
                        outputPanel.BackgroundColor = GetColor(output);

                        mCurrentPanelMouseDown = null;
                        return;
                    }
                }

                // If no dropped to another output. Disconnect the current input.
                if (input.FromOutput != null)
                {
                    var oldOutput = input.FromOutput;

                    Diconnect(input);

                    GetModulePanel(oldOutput.Module)?.UpdateColors();
                    GetModulePanel(input.Module)?.UpdateColors();
                }

                mCurrentPanelMouseDown = null;
            }
        }

        public void Connect(ModuleOutput output, ModuleInput connectToInput)
        {
            if (connectToInput is InputModuleParameter.Input_ModuleInput)
            {
                InputModuleParameter.Input_ModuleInput inputModuleInput = (InputModuleParameter.Input_ModuleInput)connectToInput;

                if(inputModuleInput.Owner.IsAutomated)
                {
                    SharpDepend.Manager.DialogManager.Show(
                        "This input is automated and will be lost if you continue connect this input. No undo can be done, do you want to continue?", "Connection",
                        DialogButtons.YesNo, DialogIcon.Warning, Connect, new object[] { output, connectToInput });
                    return;
                }
            }

            ConnectOutputCommand command = new ConnectOutputCommand(this, output, connectToInput);
            UndoRedo.AddAndRunCommand(command);
        }

        void Connect(DialogButtonResult clickedButton, object parameter)
        {
            object[] data = (object[])parameter;
            ModuleOutput output = (ModuleOutput)data[0];
            ModuleInput connectToInput = (ModuleInput)data[1];

            if (clickedButton == DialogButtonResult.Yes)
            {
                ConnectOutputCommand command = new ConnectOutputCommand(this, output, connectToInput);
                UndoRedo.AddAndRunCommand(command);
            }
        }

        public void Diconnect(ModuleInput input)
        {
            DisconnectOutputCommand command = new DisconnectOutputCommand(this, input);
            UndoRedo.AddAndRunCommand(command);
        }

        /// <summary>
        /// Calls to draw connected lines between modules.
        /// </summary>
        public void Events_OnRender(IGraphicsDevice canvas, Parent parent)
        {
            //// Loop modules.
            //foreach (var item in mModulePanels)
            //{
            //    Label lbl = (Label)item.Value.FindByTag("Performance");
            //    if (lbl != null)
            //    {
            //        lbl.SetText(item.Key.LastProcessedTime.ToString() + " ms");
            //    }
            //}

            // TODO: Graphic renderers that renders in own thread can get som threading problems here while looking up in dictionaries.
            // We need to fix this soon. Make a graphic renderer on computer and strees test it and solve the problem!
            try
            {
                // Render all outputs.
                foreach (var output in mMappedOutputs)
                {
                    TexturePanel outputTexture = output.Value;

                    double middle = outputTexture.TextureHeight * 0.5;

                    double widthDelta = 1;
                    if (output.Key.Type == IOType.Waveform)
                    {
                        widthDelta = output.Key.AudioBuffer.Length / outputTexture.TextureWidth;
                    }
                    else
                    {
                        widthDelta = output.Key.AudioBuffer.Length / outputTexture.TextureWidth;
                    }

                    Vector2 lastPoint = new Vector2();
                    PeformanceHelper.MemSet(outputTexture.ManipulateTexturePixels, 0, outputTexture.ManipulateTexturePixels.Length, Color.Black.PacketSigned);

                    outputTexture.BeginEdit();

                    for (int x = 0; x < outputTexture.TextureWidth; x++)
                    {
                        int i = (int)(widthDelta * x);
                        double sample = 0;
                        sample = -output.Key.AudioBuffer[i];

                        sample = sample > 1.0 ? 1.0 : sample < -1.0 ? -1.0 : sample;

                        Vector2 point = new Vector2(x, sample * outputTexture.TextureHeight * 0.5 + middle - 1);

                        point.X = point.X > outputTexture.TextureWidth ? outputTexture.TextureWidth : point.X < 1.0 ? 1.0 : point.X;
                        point.Y = point.Y > outputTexture.TextureHeight ? outputTexture.TextureHeight : point.Y < 0 ? 0 : point.Y;

                        if (x == 0)
                        {
                            lastPoint = point;
                        }

                        if (point.Y < middle)
                        {
                            for (int p = (int)middle; p > (int)point.Y; p--)
                            {
                                int index = (int)(outputTexture.TextureWidth * p + (int)point.X);
                                outputTexture.ManipulateTexturePixels[index] = Color.White.PacketSigned;
                            }
                        }
                        else
                        {
                            for (int p = (int)middle; p < (int)point.Y; p++)
                            {
                                int index = (int)(outputTexture.TextureWidth * p + (int)point.X);
                                outputTexture.ManipulateTexturePixels[index] = Color.White.PacketSigned;
                            }
                        }
                        lastPoint = point;
                    }

                    outputTexture.EndEdit();

                    if (output.Key.HasGotMidiMessage())
                    {
                        outputTexture.Width = 50;
                    }
                    else
                    {
                        outputTexture.Width = ELEMENT_HEIGHT;
                    }
                }

                // Loop all inputs in the current module.
                foreach (var inputPair in mMappedInputs)
                {
                    var input2 = inputPair.Key;
                    var inputModule = input2.Module;

                    // If connected.
                    if (input2.IsConnected)
                    {
                        // Draw from this module to the connected module.
                        ModuleInput m1 = input2;
                        var input = input2.FromOutput;
                        {
                            ModuleOutput m2 = input;

                            Color color = GetColor(input);

                            Panel p1 = mMappedInputs[m1];

                            if (!mMappedOutputs.ContainsKey(m2))
                            {
                                // Draw dummy lines.
                                // This input is connected to another output outside the current group module.
                                if (input.IsConnected)
                                {
                                    Vector2 dummyPos1 = new Vector2(0, p1.RealY + p1.RealHeight * 0.5);
                                    Vector2 dummyPos2 = new Vector2(p1.RealX, p1.RealY + p1.RealHeight * 0.5);

                                    canvas.DrawLine(dummyPos1, dummyPos2, color);
                                }
                                continue;
                            }
                        }
                    }
                }

                // Loop all outputs in the current module.
                foreach (var outputPair in mMappedOutputs)
                {
                    //foreach (var output in modulePair.Value)
                    {
                        var output = outputPair.Key;
                        var outputModule = output.Module;

                        // If connected.
                        if (output.IsConnected)
                        {
                            // Draw from this module to the connected module.
                            ModuleOutput m1 = output;
                            foreach (var input in output.ConnectedInputs)
                            {
                                ModuleInput m2 = input;

                                Color color = GetColor(input);

                                Panel p1 = mMappedOutputs[m1];

                                // TODO: Ifall det blir fler kopplingar, lägg dom i rad på varandra med mellanrun.
                                if (!mMappedInputs.ContainsKey(m2))
                                {
                                    // This input is connected to another output outside the current group module.
                                    if (input.IsConnected)
                                    {
                                        Vector2 dummyPos1 = new Vector2(p1.Parent.RealX + p1.Parent.RealWidth, p1.RealY + p1.RealHeight * 0.5);
                                        Vector2 dummyPos2 = new Vector2(GraphicsHost.GraphicsDevice.RenderWidth, p1.RealY + p1.RealHeight * 0.5);

                                        canvas.DrawLine(dummyPos1, dummyPos2, color);
                                    }

                                    continue;
                                }

                                Panel p2 = mMappedInputs[m2];

                                Vector2 pos1 = new Vector2(p1.Parent.RealX + p1.Parent.RealWidth, p1.RealY + p1.RealHeight * 0.5);
                                Vector2 pos2 = new Vector2(p2.RealX, p2.RealY + p2.RealHeight * 0.5);

                                DrawCurvedLine(canvas, pos1, pos2, color);
                            }
                        }
                    }
                }
            }
            catch (Exception)
            {

                throw;
            }
        }

        internal static Color GetColor(ModuleIO moduleIO)
        {
            switch (moduleIO.ActualType)
            {
                case IOType.Audio:
                    return ModulePanel.COLOR_AUDIO;
                case IOType.Midi:
                    return ModulePanel.COLOR_MIDI;
                case IOType.Waveform:
                    return ModulePanel.COLOR_WAVEFORM;
                case IOType.AudioOrWaveform:
                    return ModulePanel.COLOR_AUDIO_OR_WAVEFORM;
            }

            return Color.Pink;
        }

        static void DrawCurvedLine(IGraphicsDevice canvas, Vector2 pos1, Vector2 pos2, Color color)
        {
            double distance = SharpDepend.Utils.Math.Distance(pos1, pos2);
            int quality = System.Math.Min(80, System.Math.Max(3, (int)(distance * 0.05)));

            Vector2 center = pos1 + (pos2 - pos1) * 0.5;
            Vector2 middleUp = new Vector2(center.X, pos2.Y);
            Vector2 middleDown = new Vector2(center.X, pos1.Y);

            CurvedLine(canvas, pos1, middleDown, middleUp, pos2, color, quality);
        }

        static void CurvedLine(IGraphicsDevice canvas, Vector2 a, Vector2 b, Vector2 c, Color color, int quality)
        {
            Vector2 lastCurvedDot = a;

            for (int i = 0; i < quality + 1; i++)
            {
                double percent = 1.0 / quality * i;

                // Step 1.
                Vector2 pointA = Leap(a, b, percent);
                Vector2 pointB = Leap(b, c, percent);

                // Step 2.
                Vector2 curvedDot = Leap(pointA, pointB, percent);

                canvas.DrawLine(lastCurvedDot, curvedDot, color);

                lastCurvedDot = curvedDot;
            }
        }

        static void CurvedLine(IGraphicsDevice canvas, Vector2 a, Vector2 b, Vector2 c, Vector2 d, Color color, int quality)
        {
            Vector2 lastCurvedDot = a;

            for (int i = 0; i < quality + 1; i++)
            {
                double percent = 1.0 / quality * i;

                // Step 1.
                Vector2 pointA = Leap(a, b, percent);
                Vector2 pointB = Leap(b, c, percent);
                Vector2 pointC = Leap(c, d, percent);

                // Step 2.
                Vector2 curvedA = Leap(pointA, pointB, percent);
                Vector2 curvedB = Leap(pointB, pointC, percent);

                // Step 3.
                Vector2 curvedDot = Leap(curvedA, curvedB, percent);
                
                canvas.DrawLine(lastCurvedDot, curvedDot, color);

                lastCurvedDot = curvedDot;
            }
        }
        
        static Vector2 Leap(Vector2 a, Vector2 b, double percent)
        {
            return a + (b - a) * percent;
        }

        public void OnAddedModule(BaseModule module)
        {
        }

        public void OnRemovedModule(BaseModule module)
        {
        }

        public void SetCustomData(CustomNode custom)
        {
            if (custom.GetPropertyValue("ui", false))
            {
                mInitShowUI = true;
                ShowUI();
            }
        }

        public void GetCustomData(CustomNode customData)
        {
            customData.AddProperty("ui", innerGuiPanel == null ? mInitShowUI : innerGuiPanel.Visible);
        }

        private bool mInitShowUI;
    }
}
